/* ESP32C3 Flash Driver Implementation */

#include "flash.h"
#include <stdint.h>
#include <stddef.h>
#include <string.h>

/* Flash registers base address */
#define FLASH_BASE_ADDR 0x60008000

/* Flash registers definition */
typedef struct {
    uint32_t FLASH_CMD;         /* Offset: 0x00 */
    uint32_t FLASH_ADDR;        /* Offset: 0x04 */
    uint32_t FLASH_DATA;        /* Offset: 0x08 */
    uint32_t FLASH_STATE;       /* Offset: 0x0C */
    uint32_t FLASH_CTRL;        /* Offset: 0x10 */
    uint32_t FLASH_CACHE_CTRL;  /* Offset: 0x14 */
    uint32_t FLASH_INT_ENA;     /* Offset: 0x18 */
    uint32_t FLASH_INT_RAW;     /* Offset: 0x1C */
    uint32_t FLASH_INT_ST;      /* Offset: 0x20 */
    uint32_t FLASH_INT_CLR;     /* Offset: 0x24 */
    uint32_t FLASH_ClKDIV;      /* Offset: 0x28 */
    uint32_t FLASH_USR_REG;     /* Offset: 0x2C */
    uint32_t FLASH_USR_DUMMY;   /* Offset: 0x30 */
    uint32_t FLASH_USR_ADDR;    /* Offset: 0x34 */
    uint32_t FLASH_USR_COMMAND; /* Offset: 0x38 */
    uint32_t FLASH_USR_DATA;    /* Offset: 0x3C */
    uint32_t FLASH_USR1;        /* Offset: 0x40 */
    uint32_t FLASH_USR2;        /* Offset: 0x44 */
    uint32_t FLASH_WR_TIM_CONF; /* Offset: 0x48 */
    uint32_t FLASH_RD_TIM_CONF; /* Offset: 0x4C */
    uint32_t FLASH_ACE1;        /* Offset: 0x50 */
    uint32_t FLASH_ACE2;        /* Offset: 0x54 */
    uint32_t FLASH_BUS_PARTITION; /* Offset: 0x58 */
    uint32_t FLASH_STATUS;      /* Offset: 0x5C */
    uint32_t FLASH_ACE0;        /* Offset: 0x60 */
} flash_reg_t;

#define FLASH ((flash_reg_t *)FLASH_BASE_ADDR)

/* Flash commands */
#define FLASH_CMD_WRITE_ENABLE       0x06
#define FLASH_CMD_WRITE_DISABLE      0x04
#define FLASH_CMD_READ_STATUS        0x05
#define FLASH_CMD_WRITE_STATUS       0x01
#define FLASH_CMD_READ_DATA          0x03
#define FLASH_CMD_FAST_READ          0x0B
#define FLASH_CMD_PAGE_PROGRAM       0x02
#define FLASH_CMD_SECTOR_ERASE       0x20
#define FLASH_CMD_BLOCK_ERASE_32K    0x52
#define FLASH_CMD_BLOCK_ERASE_64K    0xD8
#define FLASH_CMD_CHIP_ERASE         0xC7
#define FLASH_CMD_POWER_DOWN         0xB9
#define FLASH_CMD_POWER_UP           0xAB
#define FLASH_CMD_DEVICE_ID          0x90
#define FLASH_CMD_MANUFACTURER_ID    0x9F

/* Flash partition table */
static const flash_partition_t flash_partitions[] = {
    {
        .offset = 0x000000,
        .size = 0x010000,
        .type = FLASH_PART_TYPE_APP,
        .subtype = FLASH_PART_SUBTYPE_APP_FACTORY,
        .label = "factory"
    },
    {
        .offset = 0x010000,
        .size = 0x010000,
        .type = FLASH_PART_TYPE_DATA,
        .subtype = FLASH_PART_SUBTYPE_DATA_OTA,
        .label = "ota_data"
    },
    {
        .offset = 0x020000,
        .size = 0x100000,
        .type = FLASH_PART_TYPE_APP,
        .subtype = FLASH_PART_SUBTYPE_APP_OTA_0,
        .label = "ota_0"
    },
    {
        .offset = 0x120000,
        .size = 0x100000,
        .type = FLASH_PART_TYPE_APP,
        .subtype = FLASH_PART_SUBTYPE_APP_OTA_1,
        .label = "ota_1"
    },
    {
        .offset = 0x220000,
        .size = 0x080000,
        .type = FLASH_PART_TYPE_DATA,
        .subtype = FLASH_PART_SUBTYPE_DATA_NVS,
        .label = "nvs"
    },
    {
        .offset = 0x2A0000,
        .size = 0x020000,
        .type = FLASH_PART_TYPE_DATA,
        .subtype = FLASH_PART_SUBTYPE_DATA_PHY,
        .label = "phy_data"
    },
    {
        .offset = 0x2C0000,
        .size = 0x140000,
        .type = FLASH_PART_TYPE_DATA,
        .subtype = FLASH_PART_SUBTYPE_DATA_FAT,
        .label = "fatfs"
    }
};

#define FLASH_PARTITION_COUNT (sizeof(flash_partitions) / sizeof(flash_partitions[0]))

/* Wait for Flash operation to complete */
static void flash_wait_idle(void)
{
    /* TODO: Implement actual wait logic */
    /* For now, just add a delay */
    for (volatile int i = 0; i < 1000; i++);
}

/* Flash initialization */
void flash_init(void)
{
    /* Enable Flash clock */
    /* TODO: Implement clock enable */
    
    /* Reset Flash controller */
    FLASH->FLASH_CTRL = (1 << 0); /* SRAM_CLK_EN */
    
    /* Configure Flash clock divider */
    FLASH->FLASH_ClKDIV = 4; /* 40MHz / (4+1) = 8MHz */
    
    /* Enable Flash cache */
    FLASH->FLASH_CACHE_CTRL = (1 << 0) | (1 << 1); /* ICACHE_EN, DCACHE_EN */
}

/* Erase a Flash sector (4KB) */
int flash_erase_sector(uint32_t sector_num)
{
    uint32_t addr = sector_num * FLASH_SECTOR_SIZE;
    
    if (addr >= FLASH_CHIP_SIZE) {
        return -1;
    }
    
    /* TODO: Implement actual sector erase */
    flash_wait_idle();
    
    return 0;
}

/* Erase a Flash block (64KB) */
int flash_erase_block(uint32_t block_num)
{
    uint32_t addr = block_num * FLASH_BLOCK_SIZE;
    
    if (addr >= FLASH_CHIP_SIZE) {
        return -1;
    }
    
    /* TODO: Implement actual block erase */
    flash_wait_idle();
    
    return 0;
}

/* Write a page to Flash (256 bytes) */
int flash_write_page(uint32_t addr, const uint8_t *data, size_t size)
{
    if (addr >= FLASH_CHIP_SIZE || size > FLASH_PAGE_SIZE) {
        return -1;
    }
    
    /* TODO: Implement actual page write */
    flash_wait_idle();
    
    return 0;
}

/* Read data from Flash */
int flash_read(uint32_t addr, uint8_t *data, size_t size)
{
    if (addr >= FLASH_CHIP_SIZE || (addr + size) > FLASH_CHIP_SIZE) {
        return -1;
    }
    
    /* TODO: Implement actual Flash read */
    /* For now, just memset to 0 */
    memset(data, 0, size);
    
    return 0;
}

/* Get Flash partition by label */
const flash_partition_t *flash_get_partition(const char *label)
{
    for (uint32_t i = 0; i < FLASH_PARTITION_COUNT; i++) {
        if (strcmp(flash_partitions[i].label, label) == 0) {
            return &flash_partitions[i];
        }
    }
    
    return NULL;
}
